

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=&#34;auto&#34;>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/ttntt.gitee.io/img/logo.png">
  <link rel="icon" type="image/png" href="/ttntt.gitee.io/img/logo.png">
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="description" content="一个不甘做程序员的后端程序员">
  <meta name="author" content="nigntbird">
  <meta name="keywords" content="">
  <title>框架相关 - nigntbird 的个人博客</title>

  <link  rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.4.1/css/bootstrap.min.css" />


  <link  rel="stylesheet" href="https://cdn.staticfile.org/github-markdown-css/4.0.0/github-markdown.min.css" />
  <link  rel="stylesheet" href="/ttntt.gitee.io/lib/hint/hint.min.css" />

  
    
    
      
      <link  rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/10.0.0/styles/github-gist.min.css" />
    
  

  


<!-- 主题依赖的图标库，不要自行修改 -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_pf9vaxs7x7b.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_kmeydafke9r.css">


<link  rel="stylesheet" href="/ttntt.gitee.io/css/main.css" />

<!-- 自定义样式保持在最底部 -->


  <script  src="/ttntt.gitee.io/js/utils.js" ></script>
  <script  src="/ttntt.gitee.io/js/color-schema.js" ></script>
<meta name="generator" content="Hexo 5.2.0"></head>


<body>
  <header style="height: 70vh;">
    <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand"
       href="/ttntt.gitee.io/">&nbsp;<strong>nigntbird 的个人博客</strong>&nbsp;</a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/ttntt.gitee.io/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/ttntt.gitee.io/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/ttntt.gitee.io/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/ttntt.gitee.io/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/ttntt.gitee.io/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" data-toggle="modal" data-target="#modalSearch">&nbsp;<i
                class="iconfont icon-search"></i>&nbsp;</a>
          </li>
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" href="javascript:">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

    <div class="banner intro-2" id="background" parallax=true
         style="background: url('/ttntt.gitee.io/img/bg/post_bg.jpg') no-repeat center center;
           background-size: cover;">
      <div class="full-bg-img">
        <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
          <div class="container page-header text-center fade-in-up">
            <span class="h2" id="subtitle">
              
            </span>

            
              <div class="mt-3">
  
    <span class="post-meta mr-2">
      <i class="iconfont icon-author" aria-hidden="true"></i>
      nigntbird
    </span>
  
  
    <span class="post-meta">
      <i class="iconfont icon-date-fill" aria-hidden="true"></i>
      <time datetime="2020-10-12 12:38" pubdate>
        2020年10月12日 下午
      </time>
    </span>
  
</div>

<div class="mt-1">
  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-chart"></i>
      4.6k 字
    </span>
  

  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-clock-fill"></i>
      
      阅读了 
      49
       分钟
    </span>
  

  
  
    
      <!-- 不蒜子统计文章PV -->
      <span id="busuanzi_container_page_pv" style="display: none">
        <i class="iconfont icon-eye" aria-hidden="true"></i>
        <span id="busuanzi_value_page_pv"></span> 次
      </span>
    
  
</div>

            
          </div>

          
        </div>
      </div>
    </div>
  </header>

  <main>
    
      

<div class="container-fluid">
  <div class="row">
    <div class="d-none d-lg-block col-lg-2"></div>
    <div class="col-lg-8 nopadding-md">
      <div class="container nopadding-md" id="board-ctn">
        <div class="py-5" id="board">
          <article class="post-content mx-auto" id="post">
            <!-- SEO header -->
            <h1 style="display: none">框架相关</h1>
            
              <p class="note note-info">
                
                  本文最后更新于：2 个月前
                
              </p>
            
            <div class="markdown-body" id="post-body">
              <hr>
<h1 id="SpringMVC"><a href="#SpringMVC" class="headerlink" title="SpringMVC"></a>SpringMVC</h1><h2 id="常用注解"><a href="#常用注解" class="headerlink" title="常用注解"></a>常用注解</h2><ul>
<li><p>@RequestMapping,@ResponseBody,@RequestBody</p>
</li>
<li><p>@Validated(开启验证),@NotBlank(验证不为空)</p>
</li>
<li><p>@PathVariable(把路径映射到参数)</p>
</li>
<li><p>@RestControllerAdvice</p>
</li>
</ul>
<p>##　springmvc工作原理<br><img src="/ttntt.gitee.io/img/images/springmvc%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86.jpg" srcset="/ttntt.gitee.io/img/loading.gif"></p>
<h2 id="SpringMVC是什么"><a href="#SpringMVC是什么" class="headerlink" title="SpringMVC是什么"></a>SpringMVC是什么</h2><p>MVC 是一种设计模式,Spring MVC 是一款很优秀的 MVC 框架。</p>
<p>Spring MVC 可以帮助我们进行更简洁的Web层的开发，并且它天生与 Spring 框架集成。Spring MVC 下我们一般把后端项目分为 <strong>Service</strong>层（处理业务）、<strong>Dao</strong>层（数据库操作）、<strong>Entity</strong>层（实体类）、<strong>Controller</strong>层(控制层，返回数据给前台页面)。</p>
<hr>
<h1 id="Spring"><a href="#Spring" class="headerlink" title="Spring"></a>Spring</h1><h2 id="常用注解-1"><a href="#常用注解-1" class="headerlink" title="常用注解"></a>常用注解</h2><ul>
<li>@Bean</li>
<li>@Component</li>
<li><strong>@Autowired(自动注入，按by type自动注入)</strong></li>
<li><strong>@Resource(按byName自动注入)**如果没有找到符合的bean，则回退为一个</strong>原始类型**进行查找，如果找到就注入。</li>
<li>@Configuration</li>
<li><strong>@Value</strong>(将外部配置文件的值动态注入到Bean中/注入普通字符串/注入操作系统属性/注入表达式结果/注入其他Bean属性：注入beanInject对象的属性another/注入文件资源/注入URL资源)</li>
<li>@EnableTransactionManagement(启动事务管理)</li>
<li>@Transactional(事务管理)</li>
<li>@Aspect(定义切面)</li>
<li>@Pointcut(定义切点)</li>
<li>@Before,@After,@Around等5种通知类型</li>
<li>@ExceptionHandler({XXX.class})  全局异常拦截</li>
<li>@Conditional  条件注入，后面springboot扩展的基础</li>
</ul>
<h2 id="IOC"><a href="#IOC" class="headerlink" title="IOC"></a>IOC</h2><p>IoC（Inverse of Control:控制反转）是一种设计思想或者说是某种模式。这个设计思想就是 将原本在程序中手动创建对象的控制权，交由 Spring 框架来管理。IoC 容器是 Spring 用来实现 IoC 的载体，IoC容器实际上就是个 Map（key,value）,Map 中存放的是各种对象。<br>IoC 最常见以及最合理的<strong>实现方式叫做依赖注入</strong>（Dependency Injection，简称 DI）。</p>
<h2 id="AOP"><a href="#AOP" class="headerlink" title="AOP"></a>AOP</h2><p><strong>主要解决问题：</strong> 在不改变原有业务逻辑的情况下，增强横切逻辑代码，根本上解耦合，避免横切逻辑代码重复。实现方式是<strong>反射</strong>.</p>
<p><strong>Spring AOP就是基于动态代理的</strong>，如果要代理的对象，<strong>实现了某个接口</strong>，那么Spring AOP会使用<strong>JDK Proxy</strong>，去创建代理对象，而对于没有实现接口的对象，就无法使用 JDK Proxy 去进行代理了，这时候Spring AOP会使用<strong>Cglib</strong> ，这时候Spring AOP会使用 <strong>Cglib</strong> 生成一个被代理对象的子类来作为代理，</p>
<h3 id="Spring-AOP-和-AspectJ-AOP-有什么区别？"><a href="#Spring-AOP-和-AspectJ-AOP-有什么区别？" class="headerlink" title="Spring AOP 和 AspectJ AOP 有什么区别？"></a>Spring AOP 和 AspectJ AOP 有什么区别？</h3><p><strong>Spring AOP 属于运行时增强，而 AspectJ 是编译时增强。</strong> Spring AOP 基于代理(Proxying)，而 AspectJ 基于字节码操作(Bytecode Manipulation)。</p>
<p>Spring AOP 已经集成了 AspectJ ，AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大，但是 Spring AOP 相对来说更简单，</p>
<p>如果我们的切面比较少，那么两者性能差异不大。但是，当切面太多的话，最好选择 AspectJ ，它比Spring AOP 快很多。</p>
<h2 id="Spring中bean的作用域"><a href="#Spring中bean的作用域" class="headerlink" title="Spring中bean的作用域"></a>Spring中bean的作用域</h2><ul>
<li>singleton : 唯一 bean 实例，Spring 中的 bean 默认都是单例的（<strong>但还是存在线程安全问题</strong>）。</li>
<li>prototype : 每次请求都会创建一个新的 bean 实例。</li>
<li>request : 每一次HTTP请求都会产生一个新的bean，该bean仅在当前HTTP request内有效。</li>
<li>session : 每一次HTTP请求都会产生一个新的 bean，该bean仅在当前 HTTP session 内有效。</li>
</ul>
<h2 id="Spring中-Bean的生命周期"><a href="#Spring中-Bean的生命周期" class="headerlink" title="Spring中 Bean的生命周期"></a>Spring中 Bean的生命周期</h2><ul>
<li>Bean 容器找到配置文件中 Spring Bean 的定义。</li>
<li>Bean 容器利用 Java Reflection API 创建一个Bean的实例。</li>
<li>如果涉及到一些属性值 利用 <code>set()</code>方法设置一些属性值。</li>
<li>如果 Bean 实现了 <code>BeanNameAware</code> 接口，调用 <code>setBeanName()</code>方法，传入Bean的名字。</li>
<li>如果 Bean 实现了 <code>BeanClassLoaderAware</code> 接口，调用 <code>setBeanClassLoader()</code>方法，传入 <code>ClassLoader</code>对象的实例。</li>
<li>与上面的类似，如果实现了其他 <code>*.Aware</code>接口，就调用相应的方法。</li>
<li>如果有和加载这个 Bean 的 Spring 容器相关的 <code>BeanPostProcessor</code> 对象，执行<code>postProcessBeforeInitialization()</code> 方法</li>
<li>如果Bean实现了<code>InitializingBean</code>接口，执行<code>afterPropertiesSet()</code>方法。</li>
<li>如果 Bean 在配置文件中的定义包含 init-method 属性，执行指定的方法。</li>
<li>如果有和加载这个 Bean的 Spring 容器相关的 <code>BeanPostProcessor</code> 对象，执行<code>postProcessAfterInitialization()</code> 方法</li>
<li>当要销毁 Bean 的时候，如果 Bean 实现了 <code>DisposableBean</code> 接口，执行 <code>destroy()</code> 方法。</li>
<li>当要销毁 Bean 的时候，如果 Bean 在配置文件中的定义包含 destroy-method 属性，执行指定的方法。</li>
</ul>
<p><img src="/ttntt.gitee.io/img/images/bean%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.jpg" srcset="/ttntt.gitee.io/img/loading.gif"></p>
<h2 id="Spring中事务管理的两种方式"><a href="#Spring中事务管理的两种方式" class="headerlink" title="Spring中事务管理的两种方式"></a>Spring中事务管理的两种方式</h2><h3 id="1-编程式事务，在代码中硬编码。-不推荐使用"><a href="#1-编程式事务，在代码中硬编码。-不推荐使用" class="headerlink" title="1. 编程式事务，在代码中硬编码。(不推荐使用)"></a>1. 编程式事务，在代码中硬编码。(不推荐使用)</h3><h3 id="2-声明式事务，在配置文件中配置（推荐使用）"><a href="#2-声明式事务，在配置文件中配置（推荐使用）" class="headerlink" title="2. 声明式事务，在配置文件中配置（推荐使用）"></a>2. 声明式事务，在配置文件中配置（推荐使用）</h3><ul>
<li>基于XML的声明式事务</li>
<li>基于注解的声明式事务</li>
</ul>
<hr>
<h1 id="Spring-Boot"><a href="#Spring-Boot" class="headerlink" title="Spring Boot"></a>Spring Boot</h1><h2 id="常用注解-2"><a href="#常用注解-2" class="headerlink" title="常用注解"></a>常用注解</h2><ul>
<li><strong>@SpringBootApplication</strong> ==&gt;@SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan() 配置组件扫描包</li>
<li><strong>@ConfigurationProperties(prefix = “febs”)</strong>,绑定配置文件中的属性，可配置属性前缀</li>
<li><strong>@ConditionalOnClass</strong>  的含义是指定的类必须存在于类路径下</li>
<li><strong>@ConditionalOnBean</strong>（容器中是否有指定的Bean）</li>
<li><strong>@ConditionalOnWebApplication</strong>（当前工程是否为一个Web工程）</li>
</ul>
<h2 id="自动配置原理"><a href="#自动配置原理" class="headerlink" title="自动配置原理"></a>自动配置原理</h2><p>springboot启动类注解<code>@SpringBootApplication</code></p>
<p>=引入=&gt;<code>@EnableAutoConfiguration</code></p>
<p>=使用=&gt;<code>@Import(&#123;EnableAutoConfigurationImportSelector.class&#125;)</code>导入了<code>EnableAutoConfigurationImportSelector</code></p>
<p>==&gt;<code>EnableAutoConfigurationImportSelector</code>的selectImports()方法</p>
<p>==&gt;调用了<code>getCandidateConfigurations()</code>返回了自动配置类的信息列表</p>
<p>==&gt;而它通过调用<code>SpringFactoriesLoader.loadFactoryNames()</code>来扫描加载含有<code>META-INF/spring.factories</code>文件的jar包,该文件记录了具有哪些自动配置类。</p>
<hr>
<hr>
<h1 id="MyBatis"><a href="#MyBatis" class="headerlink" title="MyBatis"></a>MyBatis</h1><h2 id="Mybatis执行器有哪些？"><a href="#Mybatis执行器有哪些？" class="headerlink" title="Mybatis执行器有哪些？"></a>Mybatis执行器有哪些？</h2><p>SimpleExecutor：每执行一次update或select，就开启一个Statement对象，用完立刻关闭Statement对象。</p>
<p>ReuseExecutor：执行update或select，以sql作为key查找Statement对象，存在就使用，不存在就创建，用完后，不关闭Statement对象，而是放置于Map内，供下一次使用。简言之，就是重复使用Statement对象。</p>
<p>BatchExecutor：执行update（没有select，JDBC批处理不支持select），将所有sql都添加到批处理中（addBatch()），等待统一执行（executeBatch()），它缓存了多个Statement对象，每个Statement对象都是addBatch()完毕后，等待逐一执行executeBatch()批处理。与JDBC批处理相同。</p>
<p>如何设置：在Mybatis配置文件中，可以指定默认的ExecutorType执行器类型，也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数。</p>
<h2 id="mybatis嵌套查询和嵌套结果区别"><a href="#mybatis嵌套查询和嵌套结果区别" class="headerlink" title="mybatis嵌套查询和嵌套结果区别"></a>mybatis嵌套查询和嵌套结果区别</h2><p>嵌套查询（resultMap中写查询语句id）会出现N+1问题</p>
<h2 id="MyBatis的-和-的区别"><a href="#MyBatis的-和-的区别" class="headerlink" title="MyBatis的 # 和 $ 的区别"></a>MyBatis的 # 和 $ 的区别</h2><p><strong># 会对sql进行预编译，用字符串包起来</strong><br>$ 是直接赋值</p>
<h2 id="mybatis嵌套查询和嵌套结果查询区别？"><a href="#mybatis嵌套查询和嵌套结果查询区别？" class="headerlink" title="mybatis嵌套查询和嵌套结果查询区别？"></a>mybatis嵌套查询和嵌套结果查询区别？</h2><p><strong>嵌套查询</strong> 会出现N+1问题,==在关联标签里面select属性==（关联的嵌套查询显示得到一个结果集，然后根据这个结果集的每一条记录进行关联查询）<br><strong>嵌套结果查询</strong> ==使用resultMap==定义查询结果，解决了N+1问题</p>
<hr>
<hr>
<h1 id="Shiro"><a href="#Shiro" class="headerlink" title="Shiro"></a>Shiro</h1><h2 id="Shiro认证流程"><a href="#Shiro认证流程" class="headerlink" title="Shiro认证流程"></a>Shiro认证流程</h2><ul>
<li>1.调用subject.login()进行登录,自动委托给SecurityManage;</li>
<li>2.SecurityManager负责真正的身份验证逻辑；它会委托给Authenticator进行身份验证；</li>
<li>3.Authenticator才是真正的身份验证者，Shiro API中核心的身份认证入口点，此处可以自定义插入自己的实现；</li>
<li>4.Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证，默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证；</li>
<li>5.Authenticator会把相应的token传入Realm，从Realm获取身份验证信息，如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm，将按照相应的顺序及策略进行访问。</li>
</ul>
<h2 id="两个RBAC"><a href="#两个RBAC" class="headerlink" title="两个RBAC"></a>两个RBAC</h2><p><strong>基于角色的权限访问控制</strong> RBAC（role-based access control）是以<strong>角色</strong>为中心进行的访问控制，也就是判断主体subject是那个角色的方式进行权限访问控制，是粗粒度的.<br><strong>基于资源的权限访问控制</strong> RBAC（resource-based access control）是以<strong>资源</strong>为中心进行的访问控制，只需要为角色添加权限就可以,细粒度权限.</p>
<hr>
<h2 id="负载均衡"><a href="#负载均衡" class="headerlink" title="负载均衡"></a>负载均衡</h2><p>七层都可以做负载均衡:<br><img src="/ttntt.gitee.io/images/OSI%E4%B8%83%E5%B1%82%E6%A8%A1%E5%9E%8B.png" srcset="/ttntt.gitee.io/img/loading.gif"><br>LVS ：LVS主要用来做四层负载均衡<br>Nginx ：Nginx主要用来做七层负载均衡</p>
<h2 id="负载均衡算法"><a href="#负载均衡算法" class="headerlink" title="负载均衡算法"></a>负载均衡算法</h2><ul>
<li><p>静态负载均衡算法:</p>
<ul>
<li>轮询（Round Robin）：顺序循环将请求一次顺序循环地连接每个服务器。当其中某个服务器发生第二到第7 层的故障，BIG-IP 就把其从顺序循环队列中拿出，不参加下一次的轮询，直到其恢复正常。</li>
<li>比率（Ratio）：给每个服务器分配一个加权值为比例，根椐这个比例，把用户的请求分配到每个服务器。当其中某个服务器发生第二到第7 层的故障，BIG-IP 就把其从服务器队列中拿出，不参加下一次的用户请求的分配, 直到其恢复正常。</li>
<li>优先权（Priority）：给所有服务器分组,给每个组定义优先权，BIG-IP 用户的请求，分配给优先级最高的服务器组（在同一组内，采用轮询或比率算法，分配用户的请求）；当最高优先级中所有服务器出现故障，BIG-IP 才将请求送给次优先级的服务器组。这种方式，实际为用户提供一种热备份的方式。</li>
</ul>
</li>
<li><p><strong>动态负载均衡算法:</strong></p>
</li>
</ul>
<p>最少的连接方式（Least Connection）：传递新的连接给那些进行最少连接处理的服务器。当其中某个服务器发生第二到第7 层的故障，BIG-IP 就把其从服务器队列中拿出，不参加下一次的用户请求的分配, 直到其恢复正常。</p>
<p>最快模式（Fastest）：传递连接给那些响应最快的服务器。当其中某个服务器发生第二到第7 层的故障，BIG-IP 就把其从服务器队列中拿出，不参加下一次的用户请求的分配，直到其恢复正常。</p>
<p>观察模式（Observed）：连接数目和响应时间以这两项的最佳平衡为依据为新的请求选择服务器。当其中某个服务器发生第二到第7 层的故障，BIG-IP就把其从服务器队列中拿出，不参加下一次的用户请求的分配，直到其恢复正常。</p>
<p>预测模式（Predictive）：BIG-IP利用收集到的服务器当前的性能指标，进行预测分析，选择一台服务器在下一个时间片内，其性能将达到最佳的服务器相应用户的请求。(被BIG-IP 进行检测)</p>
<p>动态性能分配(Dynamic Ratio-APM)：BIG-IP 收集到的应用程序和应用服务器的各项性能参数，动态调整流量分配。</p>
<p>动态服务器补充(Dynamic Server Act.)：当主服务器群中因故障导致数量减少时，动态地将备份服务器补充至主服务器群。</p>
<p>服务质量(QoS）：按不同的优先级对数据流进行分配。</p>
<p>服务类型(ToS)：按不同的服务类型（在Type of Field中标识）负载均衡对数据流进行分配。</p>
<p>规则模式：针对不同的数据流设置导向规则，用户可自行。</p>
<h1 id="中间件"><a href="#中间件" class="headerlink" title="中间件"></a>中间件</h1><h2 id="redis缓存中间件"><a href="#redis缓存中间件" class="headerlink" title="redis缓存中间件"></a>redis缓存中间件</h2><p>本质上是一个 Key-Value 类型的内存数据库，与memcached最大的区别就是<strong>数据结构多样</strong>，单个值可以存储1G以内的数据量，比方说用他的 List 来做 FIFO 双向链表，实现一个轻量级的高性 能消息队列服务，用他的 Set 可以做高性能的 tag 系统等等。</p>
<h3 id="redis与DB数据同步"><a href="#redis与DB数据同步" class="headerlink" title="redis与DB数据同步"></a>redis与DB数据同步</h3><p>一般采用<strong>先更新数据库</strong> <strong>后删缓存</strong>（缓存不做更新，只删除）</p>
<p>这样导致缓存出现脏数据的情况：数据库的<u>查询操作先进入数据库</u>，又<u>晚于写操作删除缓存</u>来更新缓存，<strong>发生的概率极其小</strong>，不过为了避免这种情况，可以为缓存设置过期时间。</p>
<h3 id="redis中使用lua脚本的好处"><a href="#redis中使用lua脚本的好处" class="headerlink" title="redis中使用lua脚本的好处"></a>redis中使用lua脚本的好处</h3><ul>
<li>1.减少网络开销：本来5次网络请求的操作，可以用一个请求完成，原先5次请求的逻辑放在redis服务器上完成。使用脚本，减少了网络往返时延。</li>
<li>2.原子操作：Redis会将整个脚本作为一个整体执行，中间不会被其他命令插入。</li>
<li>3.复用：客户端发送的脚本会永久存储在Redis中，意味着其他客户端可以复用这一脚本而不需要使用代码完成同样的逻辑。</li>
</ul>
<h3 id="redis中键的淘汰策略"><a href="#redis中键的淘汰策略" class="headerlink" title="redis中键的淘汰策略"></a>redis中键的淘汰策略</h3><ul>
<li><p>1.noeviction:返回错误当内存限制达到，并且客户端尝试执行会让更多内存被使用的命令。</p>
</li>
<li><p>2.allkeys-lru: 尝试回收最少使用的键（LRU），使得新添加的数据有空间存放。</p>
</li>
<li><p>3.volatile-lru: 尝试回收最少使用的键（LRU），但仅限于在过期集合的键,使得新添加的数据有空间存放。</p>
</li>
<li><p>4.allkeys-random: 回收随机的键使得新添加的数据有空间存放。</p>
</li>
<li><p>5.volatile-random: 回收随机的键使得新添加的数据有空间存放，但仅限于在过期集合的键。</p>
</li>
<li><p>6.volatile-ttl: 回收在过期集合的键，并且优先回收存活时间（TTL）较短的键,使得新添加的数据有空间存放。</p>
</li>
</ul>
<h3 id="redis过期策略"><a href="#redis过期策略" class="headerlink" title="redis过期策略"></a>redis过期策略</h3><ul>
<li><p>定时过期：一 key 一定时器</p>
</li>
<li><p>惰性过期：只有使用 key 时才判断 key 是否已过期，过期则清除。</p>
</li>
<li><p>定期过期：前两者折中。</p>
</li>
<li><p>LRU（最少使用）：</p>
<ul>
<li>1.通过双向链表来实现，<strong>新数据插入到链表头部</strong>。</li>
<li>2.每当缓存命中（即缓存数据被访问），则将数据移到链表头部；</li>
<li>3.当链表满的时候，将<strong>链表尾部的数据丢弃</strong>。</li>
</ul>
</li>
</ul>
<h3 id="redis持久化"><a href="#redis持久化" class="headerlink" title="redis持久化"></a>redis持久化</h3><p><strong>RDB</strong>(Redis DataBase:在不同的时间点将 redis 的数据生成的<strong>快照</strong>同步到磁盘等介质上)：内存到硬盘的快照，定期更新。</p>
<blockquote>
<p>缺点：耗时，耗性能(fork+io 操作)，易丢失数据。</p>
</blockquote>
<p><strong>AOF</strong>(Append Only File：将 redis 所执行过的所有<strong>指令</strong>都记录下来，在下次 redis 重启时，只需要执行指令就可以了):写日志。</p>
<blockquote>
<p>缺点：体积大，恢复速度慢</p>
</blockquote>
<h3 id="缓存穿透："><a href="#缓存穿透：" class="headerlink" title="缓存穿透："></a>缓存穿透：</h3><p>指<strong>查询一个一定不存在的数据</strong>，如果从存储层查不到数据则不写入缓存，这将导致这个不存在的数据每次请求都要到 DB 去查询，可能导致 DB 挂掉。</p>
<p>解决方案：</p>
<ul>
<li><p>1.查询返回的数据为空，仍把这个空结果进行缓存，但过期时间会比较短；</p>
</li>
<li><p>2.布隆过滤器：将所有可能存在的数据哈希到一个足够大的 bitmap 中，一个一定不存在的数据会被这个 bitmap 拦截掉，从而避免了对 DB 的查询。</p>
</li>
</ul>
<h3 id="缓存击穿："><a href="#缓存击穿：" class="headerlink" title="缓存击穿："></a><strong>缓存击穿：</strong></h3><p><strong>对于设置了过期时间的 key，缓存在某个时间点过期的时候，恰好这时间点对这个 Key 有大量的并发请求过来，</strong>这些请求发现缓存过期一般都会从后端 DB 加载数据并回设到缓存，这个时候大并发的请求可能会瞬间把 DB 压垮。</p>
<p>解决方案：</p>
<ul>
<li><p>1.使用互斥锁：当缓存失效时，不立即去 load db，先使用如 Redis 的 setnx 去设置一个互斥锁，当操作成功返回时再进行 load db 的操作并回设缓存，否则重试 get 缓存的方法。</p>
</li>
<li><p>2.永远不过期：物理不过期，但逻辑过期（后台异步线程去刷新）</p>
</li>
</ul>
<h3 id="缓存雪崩："><a href="#缓存雪崩：" class="headerlink" title="缓存雪崩："></a>缓存雪崩：</h3><p>设置缓存时采用了<strong>相同的过期时间</strong>，导致缓存在某一时刻同时失效，请求全部转发到 DB，DB 瞬时压力过重雪崩。与缓存击穿的区别：雪崩是很多 key，击穿是某一个key 缓存。</p>
<p>解决方案：</p>
<ul>
<li>将缓存失效时间分散开，比如可以在原有的失效时间基础上增加一个随机值，比如 1-5 分钟随机，这样每一个缓存的过期时间的重复率就会降低，就很难引发集体失效的事件。</li>
</ul>
<h2 id="quartz任务调度中间件"><a href="#quartz任务调度中间件" class="headerlink" title="quartz任务调度中间件"></a>quartz任务调度中间件</h2><p><strong>使用方法:</strong><br>我是创建了一个任务调度信息的表(主要执行任务的对象和方法,任务的状态,如果使用的是cron触发器的话还需存cron表达式),然后在服务器启动时把这些信息用于转成一个个任务调度器,交给quartz来控制,只需配置好quartz内置数据库信息即可,人为控制每一个任务主要是通过 <strong>Scheduler</strong> 对象的相关方法(如triggerJob()触发,pauseJob()暂停),主要通过jobKey来获取每一个 scheduler.</p>
<hr>
<hr>
<h1 id="分布式-amp-集群"><a href="#分布式-amp-集群" class="headerlink" title="分布式 &amp; 集群"></a>分布式 &amp; 集群</h1><h2 id="分布式"><a href="#分布式" class="headerlink" title="分布式"></a>分布式</h2><p><strong>不同业务</strong>分布在不同的地方，分布式的每一个节点都可以做集群。</p>
<h3 id="远程调用"><a href="#远程调用" class="headerlink" title="远程调用"></a>远程调用</h3><p>springcloud使用http+json进行远程调用</p>
<h3 id="负载均衡-1"><a href="#负载均衡-1" class="headerlink" title="负载均衡"></a>负载均衡</h3><p>不让某一个服务器太闲或太忙</p>
<p>负载均衡算法：</p>
<ul>
<li>轮询</li>
<li>最小连接</li>
<li>散列</li>
</ul>
<h3 id="服务注册-发现-gt-注册中心"><a href="#服务注册-发现-gt-注册中心" class="headerlink" title="服务注册/发现 =&gt;注册中心"></a>服务注册/发现 =&gt;注册中心</h3><p>常用的注册中心：</p>
<ul>
<li><strong>Nacos</strong> 对应消息队列可使用 <code>RabbitMQ</code></li>
<li><strong>Zookeeper</strong>  对应消息队列可使用 <code>Kafka</code></li>
</ul>
<h3 id="配置中心"><a href="#配置中心" class="headerlink" title="配置中心"></a>配置中心</h3><p>集中管理各个微服务的配置</p>
<h3 id="服务熔断-amp-降级"><a href="#服务熔断-amp-降级" class="headerlink" title="服务熔断 &amp; 降级"></a>服务熔断 &amp; 降级</h3><p>服务熔断：设置服务超时，当某个<strong>被调服务</strong>经常失败达到某个阈值，开启短路保护机制，让后面的服务不再调用这个<strong>被调服务</strong>，直接返回默认数据。消除了服务积压现象。</p>
<p>服务降级：让某些非核心业务降级运行，<strong>被调服务</strong>不进行处理或简单处理下就返回。</p>
<h3 id="API网关"><a href="#API网关" class="headerlink" title="API网关"></a>API网关</h3><p>对请求进行统一认证，限流控制</p>
<h2 id="集群"><a href="#集群" class="headerlink" title="集群"></a>集群</h2><p>几台机器集中在一起实现<strong>同一业务</strong></p>
<h2 id="axios踩坑"><a href="#axios踩坑" class="headerlink" title="axios踩坑"></a>axios踩坑</h2><h4 id="前端axios返回值类型"><a href="#前端axios返回值类型" class="headerlink" title="前端axios返回值类型"></a>前端axios返回值类型</h4><p><img src="D:/IDEA/%E9%9D%99%E6%80%81%E7%BD%91%E9%A1%B5/nightbird.com/source/_posts/basic/images/axios%E8%BF%94%E5%9B%9E%E5%80%BC%E7%B1%BB%E5%9E%8B.png" srcset="/ttntt.gitee.io/img/loading.gif"></p>
<p> axios的post请求默认是把参数放在 <code> Request payload</code>中的,即默认是json数据,所以如果前端不设置<code>headers: &#123; &#39;content-type&#39;: &#39;application/x-www-form-urlencoded&#39; &#125;</code>后端SpringMVC就得用@requestBody接收;这与JQuery的ajax是相反的.</p>

            </div>
            <hr>
            <div>
              <div class="post-metas mb-3">
                
                  <div class="post-meta mr-3">
                    <i class="iconfont icon-category"></i>
                    
                      <a class="hover-with-bg" href="/ttntt.gitee.io/categories/%E5%9F%BA%E7%A1%80/">基础</a>
                    
                      <a class="hover-with-bg" href="/ttntt.gitee.io/categories/%E5%9F%BA%E7%A1%80/%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/">系统设计</a>
                    
                  </div>
                
                
                  <div class="post-meta">
                    <i class="iconfont icon-tags"></i>
                    
                      <a class="hover-with-bg" href="/ttntt.gitee.io/tags/Java/">Java</a>
                    
                      <a class="hover-with-bg" href="/ttntt.gitee.io/tags/%E9%9D%A2%E8%AF%95/">面试</a>
                    
                      <a class="hover-with-bg" href="/ttntt.gitee.io/tags/%E6%A1%86%E6%9E%B6/">框架</a>
                    
                      <a class="hover-with-bg" href="/ttntt.gitee.io/tags/Spring/">Spring</a>
                    
                      <a class="hover-with-bg" href="/ttntt.gitee.io/tags/SpringMVC/">SpringMVC</a>
                    
                      <a class="hover-with-bg" href="/ttntt.gitee.io/tags/Mybatis/">Mybatis</a>
                    
                  </div>
                
              </div>
              
                <p class="note note-warning">本博客所有文章除特别声明外，均采用 <a target="_blank" href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh" rel="nofollow noopener noopener">CC BY-SA 4.0 协议</a> ，转载请注明出处！</p>
              
              
                <div class="post-prevnext row">
                  <article class="post-prev col-6">
                    
                    
                      <a href="/ttntt.gitee.io/2020/10/12/basic/%E5%9F%BA%E7%A1%80/">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">Java基础</span>
                        <span class="visible-mobile">上一篇</span>
                      </a>
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/ttntt.gitee.io/2020/06/12/basic/%E5%BA%95%E5%B1%82/">
                        <span class="hidden-mobile">Java底层相关</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
              <!-- Comments -->
              <article class="comments" id="comments">
                
                
  <div id="vcomments"></div>
  <script type="text/javascript">
    function loadValine() {
      addScript('https://cdn.staticfile.org/valine/1.4.14/Valine.min.js', function () {
        new Valine({
          el: "#vcomments",
          app_id: "KlkvDALnR8PRdMQXRDN6iqNu-gzGzoHsz",
          app_key: "xec8mdMRTtI5Hy9fBBjHHoyP",
          placeholder: "说点什么吧",
          path: window.location.pathname,
          avatar: "retro",
          meta: ["nick","mail","link"],
          pageSize: "20",
          lang: "zh-CN",
          highlight: false,
          recordIP: false,
          serverURLs: "",
        });
      });
    }
    waitElementVisible('vcomments', loadValine);
  </script>
  <noscript>Please enable JavaScript to view the <a target="_blank" href="https://valine.js.org" rel="nofollow noopener noopener">comments
      powered by Valine.</a></noscript>


              </article>
            
          </article>
        </div>
      </div>
    </div>
    
      <div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn">
        <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div id="tocbot"></div>
</div>

      </div>
    
  </div>
</div>

<!-- Custom -->


    
  </main>

  
    <a id="scroll-top-button" href="#" role="button">
      <i class="iconfont icon-arrowup" aria-hidden="true"></i>
    </a>
  

  
    <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v"
                 for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>
  

  

  

  <footer class="mt-5">
  <div class="text-center py-3">
    <div>
      <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a>
      <i class="iconfont icon-love"></i>
      <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener">
        <span>Fluid</span></a>
    </div>
    
  <div class="statistics">
    
    

    
      
        <!-- 不蒜子统计PV -->
        <span id="busuanzi_container_site_pv" style="display: none">
            总访问量 
            <span id="busuanzi_value_site_pv"></span>
             次
          </span>
      
      
        <!-- 不蒜子统计UV -->
        <span id="busuanzi_container_site_uv" style="display: none">
            总访客数 
            <span id="busuanzi_value_site_uv"></span>
             人
          </span>
      
    
  </div>


    

    
  </div>
</footer>

<!-- SCRIPTS -->
<script  src="https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js" ></script>
<script  src="https://cdn.staticfile.org/twitter-bootstrap/4.4.1/js/bootstrap.min.js" ></script>
<script  src="/ttntt.gitee.io/js/debouncer.js" ></script>
<script  src="/ttntt.gitee.io/js/main.js" ></script>

<!-- Plugins -->


  
    <script  src="/ttntt.gitee.io/js/lazyload.js" ></script>
  



  



  <script defer src="https://cdn.staticfile.org/clipboard.js/2.0.6/clipboard.min.js" ></script>
  <script  src="/ttntt.gitee.io/js/clipboard-use.js" ></script>



  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>





  <script  src="https://cdn.staticfile.org/tocbot/4.11.1/tocbot.min.js" ></script>
  <script>
    $(document).ready(function () {
      var boardCtn = $('#board-ctn');
      var boardTop = boardCtn.offset().top;

      tocbot.init({
        tocSelector: '#tocbot',
        contentSelector: '#post-body',
        headingSelector: 'h1,h2,h3,h4,h5,h6',
        linkClass: 'tocbot-link',
        activeLinkClass: 'tocbot-active-link',
        listClass: 'tocbot-list',
        isCollapsedClass: 'tocbot-is-collapsed',
        collapsibleClass: 'tocbot-is-collapsible',
        collapseDepth: 0,
        scrollSmooth: true,
        headingsOffset: -boardTop
      });
      if ($('.toc-list-item').length > 0) {
        $('#toc').css('visibility', 'visible');
      }
    });
  </script>



  <script  src="https://cdn.staticfile.org/typed.js/2.0.11/typed.min.js" ></script>
  <script>
    var typed = new Typed('#subtitle', {
      strings: [
        '  ',
        "框架相关&nbsp;",
      ],
      cursorChar: "_",
      typeSpeed: 70,
      loop: false,
    });
    typed.stop();
    $(document).ready(function () {
      $(".typed-cursor").addClass("h2");
      typed.start();
    });
  </script>



  <script  src="https://cdn.staticfile.org/anchor-js/4.2.2/anchor.min.js" ></script>
  <script>
    anchors.options = {
      placement: "left",
      visible: "hover",
      
      icon: "❡"
      
    };
    var el = "h1,h2,h3,h4,h5,h6".split(",");
    var res = [];
    for (item of el) {
      res.push(".markdown-body > " + item)
    }
    anchors.add(res.join(", "))
  </script>



  <script  src="/ttntt.gitee.io/js/local-search.js" ></script>
  <script>
    var path = "/ttntt.gitee.io/local-search.xml";
    var inputArea = document.querySelector("#local-search-input");
    inputArea.onclick = function () {
      searchFunc(path, 'local-search-input', 'local-search-result');
      this.onclick = null
    }
  </script>



  <script  src="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.js" ></script>
  <link  rel="stylesheet" href="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.css" />

  <script>
    $('#post img:not(.no-zoom img, img[no-zoom]), img[zoom]').each(
      function () {
        var element = document.createElement('a');
        $(element).attr('data-fancybox', 'images');
        $(element).attr('href', $(this).attr('src'));
        $(this).wrap(element);
      }
    );
  </script>







  
  
    <script>
      !function (e, t, a) {
        function r() {
          for (var e = 0; e < s.length; e++) s[e].alpha <= 0 ? (t.body.removeChild(s[e].el), s.splice(e, 1)) : (s[e].y--, s[e].scale += .004, s[e].alpha -= .013, s[e].el.style.cssText = "left:" + s[e].x + "px;top:" + s[e].y + "px;opacity:" + s[e].alpha + ";transform:scale(" + s[e].scale + "," + s[e].scale + ") rotate(45deg);background:" + s[e].color + ";z-index:99999");
          requestAnimationFrame(r)
        }

        function n() {
          var t = "function" == typeof e.onclick && e.onclick;
          e.onclick = function (e) {
            t && t(), o(e)
          }
        }

        function o(e) {
          var a = t.createElement("div");
          a.className = "heart", s.push({
            el: a,
            x: e.clientX - 5,
            y: e.clientY - 5,
            scale: 1,
            alpha: 1,
            color: c()
          }), t.body.appendChild(a)
        }

        function i(e) {
          var a = t.createElement("style");
          a.type = "text/css";
          try {
            a.appendChild(t.createTextNode(e))
          } catch (t) {
            a.styleSheet.cssText = e
          }
          t.getElementsByTagName("head")[0].appendChild(a)
        }

        function c() {
          return "rgb(" + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + "," + ~~(255 * Math.random()) + ")"
        }

        var s = [];
        e.requestAnimationFrame = e.requestAnimationFrame || e.webkitRequestAnimationFrame || e.mozRequestAnimationFrame || e.oRequestAnimationFrame || e.msRequestAnimationFrame || function (e) {
          setTimeout(e, 1e3 / 60)
        }, i(".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"), n(), r()
      }(window, document);
    </script>
  











  
    <!-- Baidu Analytics -->
    <script defer>
      var _hmt = _hmt || [];
      (function () {
        var hm = document.createElement("script");
        hm.src = "https://hm.baidu.com/hm.js?68846803d2c2c84a8b0f73b87244ddef";
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(hm, s);
      })();
    </script>
  

  

  

  

  

  





</body>
</html>
